package gamemodels;

import gamecomponents.Leaderboard;
import gamecomponents.LeaderboardComponent;
import gamecomponents.Time;
import gamecomponents.graphic.BlitData;
import gamecomponents.graphic.Blitable;
import gamecomponents.graphic.TextureHelper;
import gamecomponents.menu.MenuButton;
import gamecomponents.menu.MenuImage;
import gamecomponents.settings.GameSettings;
import input.InputManager;

import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.util.LinkedList;
import java.util.List;
import java.util.Observable;

import javax.swing.JOptionPane;

import com.threed.jpct.SimpleVector;
import com.threed.jpct.Texture;

/**
 * Model ran when maze is finished.
 * @author Team Deer
 *
 */
public class MazeFinishedModel extends Observable implements IGameModel {

	private static MazeFinishedModel singletonInstance;
	private IGameModel nextModel;

	private final SimpleVector screenSize;

	private final MenuImage backgroundImage;
	
	private final MenuButton mainMenuButton;
	private final MenuButton playAgainButton;

	// Set to true to stop running model
	private boolean requestExit;

	// Set to true if leaderboard is updated.
	private boolean leaderboardUpdated;
	
	private final List<Blitable> components;

	// Constructor and singleton getter
	
	private MazeFinishedModel() {
		
		this.screenSize = GameSettings.SCREEN_SIZE;

		this.requestExit = false;
		this.leaderboardUpdated = false;

		this.components = new LinkedList<Blitable>();

		// Add a background image.
		this.backgroundImage = new MenuImage(
				TextureHelper
						.loadTexture("Content\\Sprites\\Leaderboard\\finishlineBackground.png"),
				0, 0, (int) this.screenSize.x, (int) this.screenSize.y, 255);
		this.components.add(this.backgroundImage);

		// Button to go back to main menu.
		this.mainMenuButton = new MenuButton(
				TextureHelper
						.loadTexture("Content\\Sprites\\Menu\\mainMenuActive.png"),
				TextureHelper
						.loadTexture("Content\\Sprites\\Menu\\mainMenuInactive.png"),
				(int) (this.screenSize.x / 8), (int) (this.screenSize.y
						- (this.screenSize.y / 8) + 5), 378, 49, 255);
		this.components.add(this.mainMenuButton);

		// Button to play the same maze again
		this.playAgainButton = new MenuButton(
				TextureHelper
						.loadTexture("Content\\Sprites\\Menu\\playAgainActive.png"),
				TextureHelper
						.loadTexture("Content\\Sprites\\Menu\\playAgainInactive.png"),
				(int) (this.screenSize.x - (this.screenSize.x / 8) - 347),
				(int) (this.screenSize.y - (this.screenSize.y / 8) + 5), 
				347, 51, 
				255);
		this.components.add(this.playAgainButton);
		
		super.setChanged();
	}

	/**
	 * 
	 * @return the singleton instance.
	 */
	public static MazeFinishedModel getInstance() {
		if (singletonInstance == null) {
			singletonInstance = new MazeFinishedModel();
		}
		return singletonInstance;
	}

	// Update
	
	@Override
	public void reset() {
		this.requestExit = false;
		this.nextModel = null;
		this.leaderboardUpdated = false;
	}
	
	@Override
	public boolean isRunning() {
		return !this.requestExit;
	}
	
	@Override
	public void update(final InputManager inputManager) {
		if (!this.leaderboardUpdated) {

			final Leaderboard leaderboard = Leaderboard.getInstance();

			final Time time = new Time(SinglePlayerModel.getInstance()
					.getTimer().clone());

			// Time has qualified into the list
			final int scoreboardPlace = leaderboard.timeQualifies(time,
					SinglePlayerModel.getInstance().getMazeSize());
			if (scoreboardPlace >= 0) {

				final String playerName = JOptionPane
						.showInputDialog("Congratulations!\nYou are in place "
								+ (scoreboardPlace + 1)
								+ "! \nPlease enter your name.");
				final LeaderboardComponent leaderboardComponent = new LeaderboardComponent(
						time, playerName);

				leaderboard.addComponent(scoreboardPlace, leaderboardComponent, 
						SinglePlayerModel.getInstance().getMazeSize());
				leaderboard.writeLeaderboardToFile();

			}
			// Leaderboard Updated, draw the data.
			this.leaderboardUpdated = true;
			super.setChanged();
			super.notifyObservers();
			return;
		}

		// Go back to Main Menu
		if (inputManager.getKeyboardState().isKeyPressed(KeyEvent.VK_ENTER) &&
				inputManager.getKeyboardState().getPreviousKeyboardState().isKeyUp(KeyEvent.VK_ENTER)) {
			this.changeModel(MainMenuModel.getInstance());
		}

		// Let the buttons read input...
		this.updateButtons(inputManager);
		
		// Does View want to know?
		if (this.mainMenuButton.hasChanged() || 
				this.playAgainButton.hasChanged()) {
			super.setChanged();
		}
		
		super.notifyObservers();
	}
	
	private void updateButtons(final InputManager inputManager) {
		
		// Check for interaction with buttons
		this.mainMenuButton.update(inputManager);
		this.playAgainButton.update(inputManager);
		
		// Perfrom actions
		if (inputManager.mouseIsClicked()) {
			if (this.mainMenuButton.pointIsOver(inputManager.getMousePosition())) {
				this.changeModel(MainMenuModel.getInstance());
			}
			if (this.playAgainButton.pointIsOver(inputManager.getMousePosition())) {
				
				SinglePlayerModel.getInstance().createNewGame(null, true);
				this.changeModel(SinglePlayerModel.getInstance());
			}
		}
	}
	
	private void changeModel(final IGameModel model) {
		this.nextModel = model;
	}
	
	@Override
	public IGameModel getNextModel() {
		return this.nextModel;
	}
	
	// Viewhelp
	
	@Override
	public List<BlitData> getBlitData() {
		
		final List<BlitData> list = new LinkedList<BlitData>();

		for (final Blitable data : this.components) {
			list.add(new BlitData(
					data.getTexture(), // texture
					0 /* srcX */, 0 /* srcY */, data.getPosition().x /* destX */,
					data.getPosition().y /* destY */,
					data.getSrcWidth() /* sourceWidth */,
					data.getSrcHeight() /* sourceHeight */,
					data.getWidth() /* destWidth */,
					data.getHeight() /* destHeight */, 255 /* transValue */,
					false /* additive */, null)); /* addColor */
		}
		
		// Add transparent texture
		list.add(new BlitData(
				TextureHelper.getSemiTransparentTexture(), // texture
				0 /* srcX */, 0 /* srcY */,
				(int) (GameSettings.SCREEN_SIZE.x / 8) /* destX */,
				(int) (GameSettings.SCREEN_SIZE.y / 8)/* destY */,
				2 /* sourceWidth */, 2 /* sourceHeight */,
				(int) (6 * GameSettings.SCREEN_SIZE.x / 8) /* destWidth */,
				(int) (6 * GameSettings.SCREEN_SIZE.y / 8) /* destHeight */,
				255 /* transValue */, false /* additive */, null)); /* addColor */

		final BufferedImage img = new BufferedImage(
				(int) (6 * GameSettings.SCREEN_SIZE.x / 8),
				(int) (6 * GameSettings.SCREEN_SIZE.y / 8),
				BufferedImage.TYPE_INT_ARGB);
		final Graphics g = img.getGraphics();
		g.setFont(new Font("Segoe WP", Font.PLAIN, 36));

		for (int i = 0; i < Leaderboard
				.getInstance()
				.getLeaderBoardComponents(
						SinglePlayerModel.getInstance().getMazeSize()).size(); i++) {
			g.drawString(
					Leaderboard
							.getInstance()
							.getLeaderBoardComponents(
									SinglePlayerModel.getInstance()
											.getMazeSize()).get(i)
							.toFormattedString(), 20, 56 + i * 56);
		}

		final Texture leaderBoardTexture = new Texture(img);
		list.add(new BlitData(
				leaderBoardTexture, // texture
				0 /* srcX */, 0 /* srcY */,
				(int) (GameSettings.SCREEN_SIZE.x / 8) /* destX */,
				(int) (GameSettings.SCREEN_SIZE.y / 8)/* destY */,
				leaderBoardTexture.getWidth() /* sourceWidth */,
				leaderBoardTexture.getHeight() /* sourceHeight */, img
						.getWidth() /* destWidth */,
				img.getHeight() /* destHeight */, 255 /* transValue */,
				false /* additive */, null)); /* addColor */
		
		return list;
		
		
	}

}
